Skip to content

Make the C extension Ractor-safe by removing msgpack_rmem_* slab allocator#392

Open
ianks wants to merge 1 commit into
msgpack:masterfrom
ianks:ractor-safe-rmem
Open

Make the C extension Ractor-safe by removing msgpack_rmem_* slab allocator#392
ianks wants to merge 1 commit into
msgpack:masterfrom
ianks:ractor-safe-rmem

Conversation

@ianks

@ianks ianks commented Jun 10, 2026

Copy link
Copy Markdown

This makes the extension safe to use from a non-main Ractor. The notable change here is the removal of the msgpack_rmem_* routines, which serve as a mechanism for efficiently providing chunks of memory for decoding work.

Why is it not ractor safe?

The old page-recycling slab is a process-global msgpack_rmem_t mutated through an unsynchronized bitmask, so parallel Ractors (or threads) race on it.

How did you address this?

Drop the global slab entirely and use plain xmalloc/xfree. Modern arena-based mallocs (i.e. jemalloc) are good at recycling and avoiding thread contention, so maintaining a custom slab allocator is not worth it.

Did you try alternatives?

Yes:

  • Per-instance slab on the factory - turned out to be slightly slower, and more memory intensive

No:

  • Lock-free concurrent linked list - too much work for something may not show much improvement over malloc(3)

Perf (local HTTP requests)

⚠️ TAKE THESE WITH A BIG GRAIN OF SALT, THE ERROR BARS ARE BIG
⚠️ THE GOAL OF THIS PR IS RACTOR COMPATIBILITY NOT PERF

Single-threaded, jemalloc 5.3, decode-heavy workload

On a realistic, "real" HTTP request benchmark, bare-xmalloc is maybe a touch faster, but mostly noise in the diff:

warm, paired (no-slab this PR vs slab, not ractor-safe) Δ 95% CI
wall time −0.1% −0.8% to +4.9%
CPU −0.3% −1.9% to +3.9%
allocations identical

RSS Impact

Memory usage seems fine as well, with the bare-xmalloc (this PR) having a higher peak as decay time increases (this is expected, and not a problem).

sustained decode slab (today) xmalloc (this PR)
dirty_decay_ms:10000 (default) 43 MiB 102 MiB
dirty_decay_ms:1000 33 MiB 34 MiB
peak (live working set) ~113 MiB ~114 MiB

closes #390

@ianks ianks force-pushed the ractor-safe-rmem branch from cc53acc to d1c9fce Compare June 10, 2026 20:43
@ianks ianks changed the title Make the C extension Ractor-safe Make the C extension Ractor-safe by removing msgpack_rmem_* slab allocator Jun 10, 2026
@ianks ianks marked this pull request as ready for review June 10, 2026 22:06
@ianks ianks force-pushed the ractor-safe-rmem branch 2 times, most recently from 28616a5 to bb0785b Compare June 11, 2026 01:53
…or_safe)

The page-recycling slab was a process-global msgpack_rmem_t mutated through an
unsynchronized bitmask, so parallel Ractors (or threads) raced on it. Drop the
slab and serve rmem pages straight from xmalloc/xfree (jemalloc and friends
recycle these fine), then declare rb_ext_ractor_safe(true). The declaration is
guarded by HAVE_RB_EXT_RACTOR_SAFE so the extension still builds on Ruby < 3.0.

Add spec/cruby/ractor_spec.rb covering pack/unpack via Factory and
Packer/Unpacker from non-main Ractors, including a concurrent multi-Ractor
stress over the page-allocation path.
@ianks ianks force-pushed the ractor-safe-rmem branch from bb0785b to b82fefc Compare June 11, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ractor incompatibility: DefaultFactory not shareable, C extension methods marked ractor-unsafe

1 participant